package ro.dta.idbi.ui.sql;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlForeignKey;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.model.AutoMedUtils;
import ro.dta.idbi.model.Utils;

import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.reps.SchemaObject;

import com.mxgraph.model.mxGeometry;
import com.mxgraph.view.mxGraph;

/**
 * Sql Table cell used in the SQL diagram component
 * 
 * @author Tudor Dobrila
 * 
 */
public class SqlTableCell extends AbstractSqlCell implements Cloneable, ISqlTable {

	private static final long serialVersionUID = -4219703679876359329L;

	protected Point position;

	/**
	 * List of columns belonging to the table
	 */
	protected transient List<ISqlColumn> columns;

	/**
	 * List of FK constraints of the table
	 */
	protected transient List<ISqlForeignKey> fKeys;

	/**
	 * List of names of supertypes of the table
	 */
	protected transient List<String> supertypes;

	/**
	 * Supertype tables
	 */
	protected transient List<ISqlTable> superTbls;

	/**
	 * Child tables
	 */
	protected transient List<ISqlTable> childTbls;

	/**
	 * mxSqlConstruct class constructor
	 * 
	 * @param schemaObj
	 *            AutoMed Schema object associated with this construct
	 * @param ident
	 *            ID of construct
	 * @param value
	 *            Value of cell
	 * @param xPos
	 *            X position of cell
	 * @param yPos
	 *            Y position of cell
	 * @param width
	 *            Width of cell
	 * @param height
	 *            Height of cell
	 * @param style
	 *            Style of the cell
	 */
	public SqlTableCell(final Schema schema, final SchemaObject schemaObj, final String ident,
			final String value, final double xPos, final double yPos, final double width,
			final double height, final String style) {
		super(schema, schemaObj, ident, value, new mxGeometry(xPos, yPos, width, height), style);
		this.position = new Point(0, 20);
		initialise();
	}

	/**
	 * mxSqlConstruct class constructor
	 * 
	 * @param schemaObj
	 *            AutoMed Schema object associated with this construct
	 * @param ident
	 *            ID of construct
	 * @param value
	 *            Value of cell
	 * @param geometry
	 *            Geometry description of the construct
	 * @param style
	 *            Style of the cell
	 */
	public SqlTableCell(final Schema schema, final SchemaObject schemaObj, final String ident,
			final String value, final mxGeometry geometry, final String style) {
		super(schema, schemaObj, ident, value, geometry, style);
		if (geometry == null) {
			this.position = new Point(0, 0);
		} else {
			this.position = new Point((int) geometry.getX(), (int) geometry.getY());
		}
		initialise();
	}

	/**
	 * Initialise table
	 */
	private void initialise() {
		setVertex(true);
		this.columns = new ArrayList<ISqlColumn>();
		this.fKeys = new ArrayList<ISqlForeignKey>();
		this.superTbls = new ArrayList<ISqlTable>();
		this.childTbls = new ArrayList<ISqlTable>();
		if (schema != null && schemaObj != null) {
			supertypes = AutoMedUtils.getSupertypes(schema, schemaObj);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#setPosition(java.awt.Point)
	 */
	@Override
	public void setPosition(final Point position) {
		this.position = position;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#getPosition()
	 */
	@Override
	public Point getPosition() {
		return position;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#columnNameExists(java.lang.String)
	 */
	@Override
	public boolean columnNameExists(final String name) {
		boolean result = false;
		for (ISqlColumn col : columns) {
			if (col.getName().equals(name)) {
				result = true;
				break;
			}
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.sql.ui.ISqlTable#addColumn(ro.dta.idbi.sql.ui.mxSqlColumnCell
	 * )
	 */
	@Override
	public void addColumn(final ISqlColumn column) {
		if (!columnNameExists(column.getName())) {
			columns.add(column);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#addColumn(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public ISqlColumn addColumn(final String value, final String style) {
		ISqlColumn result = null;
		if (!columnNameExists(value)) {
			final SqlColumnCell newCell = new SqlColumnCell(null, null, "", value, 0,
					position.getY(), 150, 23, style);
			position.translate(0, 25);
			columns.add(newCell);
			result = newCell;
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#addBeforeColumn(ro.dta.idbi.sql.ui.
	 * mxSqlColumnCell, ro.dta.idbi.sql.ui.mxSqlColumnCell)
	 */
	@Override
	public void addBeforeColumn(final ISqlColumn column, final ISqlColumn newColumn) {
		if (!columnNameExists(newColumn.getName())) {
			final int index = columns.indexOf(column);
			if (index != -1) {
				columns.add(index, newColumn);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#addBeforeColumn(ro.dta.idbi.sql.ui.
	 * mxSqlColumnCell, java.lang.String, java.lang.String)
	 */
	@Override
	public void addBeforeColumn(final ISqlColumn column, final String value, final String style) {
		if (!columnNameExists(value)) {
			final SqlColumnCell newColumn = new SqlColumnCell(null, null, "", value, 0, 0, 150, 23,
					style);
			addBeforeColumn(column, newColumn);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.sql.ui.ISqlTable#removeColumn(ro.dta.idbi.sql.ui.mxSqlColumnCell
	 * )
	 */
	@Override
	public boolean removeColumn(final ISqlColumn column) {
		return columns.remove(column);
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		final SqlTableCell obj = (SqlTableCell) super.clone();
		obj.position = new Point(position);
		if (supertypes != null) {
			obj.supertypes = new ArrayList<String>(supertypes);
		}

		if (columns != null) {
			obj.columns = new ArrayList<ISqlColumn>();
			for (ISqlColumn col : columns) {
				obj.columns.add((SqlColumnCell) col.clone());
			}
		}

		return obj;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#getColumns()
	 */
	@Override
	public List<ISqlColumn> getColumns() {
		return columns;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#layout(com.mxgraph.view.mxGraph)
	 */
	@Override
	public void layout(final mxGraph graph) {
		if (!graph.getModel().contains(this)) {
			graph.addCell(this, graph.getDefaultParent());
		}

		for (ISqlColumn col : columns) {
			if (!graph.getModel().contains(col)) {
				graph.addCell(col, this);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.sql.ui.ISqlTable#relayout(com.mxgraph.view.mxGraph)
	 */
	@Override
	public void relayout(final mxGraph graph) {
		layout(graph);
		position = new Point(0, 20);

		for (ISqlColumn col : columns) {
			col.getGeometry().setX(0);
			col.getGeometry().setY(position.getY());
			position.translate(0, 25);
		}

		getGeometry().setWidth(150);
		getGeometry().setHeight(position.getY());

		graph.refresh();
	}

	@Override
	public ISqlColumn getColumn(final String name) {
		ISqlColumn result = null;
		for (ISqlColumn col : columns) {
			if (col.getName().equals(name)) {
				result = col;
			}
		}

		return result;
	}

	@Override
	public List<String> getSupertypesNames() {
		return supertypes;
	}

	@Override
	public void addForeignKey(final ISqlForeignKey foreignKey) {
		fKeys.add(foreignKey);
	}

	@Override
	public List<ISqlForeignKey> getForeignKeys() {
		return fKeys;
	}

	@Override
	public List<ISqlColumn> getPrimaryKey() {
		final List<ISqlColumn> result = new ArrayList<ISqlColumn>();

		for (ISqlColumn c : columns) {
			if (c.isPK()) {
				result.add(c);
			}
		}

		return result;
	}

	@Override
	public String toString() {
		return getValue().toString();
	}

	@Override
	public void addSupertype(ISqlTable table) {
		superTbls.add(table);
	}

	@Override
	public List<ISqlTable> getSupertypes() {
		return superTbls;
	}

	@Override
	public void addChild(ISqlTable o1) {
		if (childTbls != null) {
			childTbls.add(o1);
		}
	}

	@Override
	public List<ISqlTable> getChildren() {
		return childTbls;
	}

	@Override
	public String getIqlQuery() {
		StringBuilder builder = new StringBuilder("[ {");
		for (ISqlColumn c : columns) {
			builder.append(c.getName().toLowerCase()).append(",");
		}
		builder.setLength(builder.length() - 1);
		builder.append("} | ");
		for (ISqlColumn c : columns) {
			builder.append("{x,").append(c.getName().toLowerCase()	);
			builder.append("} <- ").append(c.getShortDefinition()).append("; ");
		}
		builder.setLength(builder.length() - 2);
		builder.append(" ]");
		return builder.toString();
	}

	@Override
	public String getSchemeDefinition() {
		return Utils.genTableRepresentation(getValue().toString());
	}
}
